﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;


namespace MyZip
{
    class Decompression
    {
        /*private const int DicLen = 250;//8 bits
        private const int DicBits = 8;
        private const int BufferLen = 60;//6 bits
        private const int BufferBits = 6;*/

        private const int DifBits = Consts.DifBits;
        private const int LenP = Consts.DicBits;
        private const int LenC = Consts.BufferBits;

        public Byte[] outResult; //
        public int outResultLength;

        public void DecompressionFunc(Byte[] input)
        {           
            int inPointer = 0;
            int inPointerBit = 0;
            int outPointer = 0;
            int outPointerBit = 0;
            
            Byte[] output = new Byte[input.Length*50];
            Array.Clear(output,0,output.Length);
            
        
            while( inPointer != input.Length )
            {
                 int result = judge(input, ref inPointer,ref inPointerBit);
                 if( result == 2 )
                    {
                        Byte temp = (Byte)(getInt(input,ref inPointer,ref inPointerBit,8));
                        InsertBits( output,ref outPointer,ref outPointerBit,temp,8 );
                    }
                    else if (result == 0 )
                    {
 
                        int p = getInt(input,ref inPointer,ref inPointerBit,LenP);
                        int c = getInt(input,ref inPointer,ref inPointerBit,LenC);

                        if (p >= c)
                        {
                            copyBits(output, ref outPointer, ref outPointerBit, p, c);
                        }
                        else
                        {
                            int tempp = outPointer - p;
                            int temppp = outPointerBit;
                           

                   
                            int cur = 0;
                            while (true)
                            {
                                if (cur >= c) break;
                               
                                Byte temp = (Byte)getInt(output, ref tempp, ref temppp, 8);
                                
                                InsertBits(output, ref outPointer, ref outPointerBit, (Byte)temp, 8);
                                cur++;
                                //count++;
                                // p++; ;
                            }
                            
                                
                            
                        }


                    }
                    else if (result == 3)
                    {
                        int p = getInt(input, ref inPointer, ref inPointerBit, LenP);
                        int c = getInt(input, ref inPointer, ref inPointerBit, LenC);

                        if (p >= c)
                        {
                            int tempp = outPointer - p;
                            int temppp = outPointerBit;
                            for (int i = 1; i <= c; i++)
                            {

                                int tempInt = getInt(input, ref inPointer, ref inPointerBit, 4);

                                
                                int tempOut = getInt(output, ref tempp, ref temppp, 8);

                                tempOut = tempInt - (1 << (DifBits - 1))+tempOut;
                                InsertBits(output, ref outPointer, ref outPointerBit, (Byte)tempOut, 8);

                            }
                        }
                        else
                        {
                            int tempp = outPointer - p;
                            int temppp = outPointerBit;
                            int startp = tempp;
                            int startpp = temppp;

                            int tempP = p;
                            //int count = 0;
                            int cur = 0;
                            while( true) 
                            {
                                if (cur>= c) break;
                               /* if (count >= tempP)
                                {
                                    tempp = startp;
                                    temppp = startpp;
                                    count = 0;
                                }
                               */
                               Byte temp = (Byte)getInt(output,ref tempp,ref temppp,8 );
                               int tempInt = getInt(input, ref inPointer, ref inPointerBit, 4);
                               int tempOut = tempInt - (1 << (DifBits - 1)) + temp;
                               InsertBits(output, ref outPointer, ref outPointerBit, (Byte)tempOut, 8);
                               cur++;
                               //count++;
                              // p++; ;
                            }


                        }


                    }
                    else if (result == 1)
                    {
                        break;

                    }
                }

            outResult = output;
            outResultLength = outPointer;
          // outtxt(outResult);
    }


        void outtxt(Byte[] s)
        {
            int i=0;
            FileStream aFile = new FileStream("lintest.txt ", FileMode.OpenOrCreate);
            StreamWriter sw = new StreamWriter(aFile);
            string text = " ";
            for (; i < s.Length; i++)
            {
                text = s[i]+ " ";
                Console.WriteLine(text);
                sw.Write(text);
            }
            sw.Close();

        }

    int getInt( Byte[] s, ref int p, ref int pp, int len) //len<=8
    {
        int bits1,bits2;
        if( pp + len -1<=7 )
        {
            int temp = s[p]>>(8-len-pp);
            temp = temp & ((1<<len)-1);
            //temp = temp << (8 - len - pp);
            if (pp + len == 8) p++;
            pp = (pp + len) % 8;;
            return temp;
        }
        else 
        {
            int len1 = 8-pp;
            int len2 = len - len1;
            bits1 = (s[p] & ((1<<(8-pp))-1))<<len2;
            bits2 = s[p+1]>>(8-len2);
            p++;
            pp = (pp+len)%8;
            return bits1|bits2;
        }
    }

    
    
    int judge(Byte[]s, ref int p,ref int pp )  //判断标志位
    {
        int bits1, bits2;
        if( pp==7 ) 
        {
            bits1 = (s[p]&1)*2;
            bits2 = (s[p+1]>>7)&1;
            p++;
            pp = (pp+2)%8;;
            return bits1+bits2;
        }
        else 
        {
            
            int temp = (s[p] >> (6 - pp)) & 3;
            if (pp + 2 >= 8) p++;
            pp = (pp + 2) % 8;
            return temp;
        }
    }

    void clearBits(Byte[] s, ref int p, ref int pp, int len)
    {
      
            
            for (int i = 1; i <= len+1; i++)
            {
                s[p + i] = (Byte)(s[p + i] & 0);
            }
            Byte temp = (Byte)(255 << (8-pp));
            s[p] = (Byte)(s[p] & temp);
        
    }



    void copyBits(Byte[] s, ref int t, ref int tt, int p, int c)
    {
        
        int tempp = t - p;
        int temppp = tt;
        for (int i = 1; i <= c; i++)
        {
            Byte temp = (Byte)getInt(s, ref tempp, ref temppp, 8);
            InsertBits(s, ref t, ref tt, temp, 8);
        }
    }


    void InsertBits(Byte[] Dest, ref int p, ref int pp, Byte bits, int len)
    {
        if (pp + len - 1 <= 7)
        {
            int temp = Dest[p];
            temp = temp | (bits << (8 - pp - len));
            Dest[p] = (Byte)temp;
            if (pp + len == 8)
                p++;
            pp = (pp + len) % 8;
        }
        else
        {
            int len1 = 8 - pp;
            int len2 = len - len1;
            int bits1;
            int bits2;

            bits2 = ((1 << len2) - 1) & bits;
            bits1 = bits >> len2;

            int temp = Dest[p];
            temp = temp | bits1;
            Dest[p] = (Byte)temp;
            p++; pp = 0;

            Dest[p] = (Byte)(bits2 << (8 - pp - len2));
            pp = len2;
        }
    }
           
}
}